package psd;

import java.io.*;
import java.util.*;
import java.text.*;

public class Matcher {

	static boolean filename_id_flag = false;

	public static final String HEADER_STRING_ID = "personID,IDCode,timestamp,group,groupStyle,pubname,email";
	public static final String GLOBAL_LOOK = ",lookGroup,friendship,stat,noPrimary";
	public static final String PERSON_STRING = ",firstX,lastX,isManX,isWomanX,isTransMX,isTransWX,isGQX,genderX,ageX,isKinkyX,lookMX,lookWX,lookTransMX,lookTransWX,lookGQX,lookNoneX,lookGenderX,lookKinkX,minAgeX,maxAgeX";
	public static final String PERSON_STRING_S = ",isManX,isWomanX,isTransMX,istransWX,isGQX,genderX,ageX,isKinkyX,lookMX,lookWX,lookTransMX,lookTransWX,lookGQX,lookNoneX,lookGenderX,lookKinkX,minAgeX,maxAgeX";

	public static final String LOCATIONS = ",fromEB,fromSB,fromNB,fromSF,fromPN,fromSE";

	public static final String HEADER_STRING_ALL = HEADER_STRING_ID
			+ ",addList" + GLOBAL_LOOK + PERSON_STRING.replaceAll("X", "1")
			+ PERSON_STRING.replaceAll("X", "2")
			+ PERSON_STRING.replaceAll("X", "3")
			+ PERSON_STRING.replaceAll("X", "4") + LOCATIONS
			+ ",referred,friends,remarks,geekCode,coming,notes";

	public static final String HEADER_STRING_SIMPLE = HEADER_STRING_ID
			+ GLOBAL_LOOK + PERSON_STRING_S.replaceAll("X", "1")
			+ PERSON_STRING_S.replaceAll("X", "2")
			+ PERSON_STRING_S.replaceAll("X", "3")
			+ PERSON_STRING_S.replaceAll("X", "4") + ",geekCode,coming,notes";

	public static final int NO = -1;

	public static final int UNKNOWN = 0;

	static boolean write_comments_flag = true;
	public static final int YES = 1;

	ArrayList<RegRecord> folks = new ArrayList<RegRecord>();
	File matrixFile = null;

	String matrixFileName = "matrix.csv";

	int N; // number of folks
	// int[][] m; // matrix of dating ability

	boolean outputError = false;

	File outputFile = null;

	String outputFileName = "allFolks.csv";
	String outputDescName = "allFolksDesc.txt";

	Writer outputWriter = null;

	private boolean remarks_dump_flag;

	private File outputDesc;

	private PrintWriter outputDescWriter;

	private int[][] allM;

	private String testFileName;

	/**
	 * Set things up and stash varaibles as to where
	 * to put files, etc.
	 * @param args
	 */
	public Matcher( String[] args ) {
		for (int i = 0; i < args.length; i++) {
			String arg = args[i];

			if (arg == null)
				continue;

			if (arg.startsWith("-no")) {
				write_comments_flag = false;
				continue;
			}

			if (arg.startsWith("-fi")) {
				filename_id_flag = true;
				continue;
			}

			if ( arg.startsWith( "-t" )) {
				if ( args[i+1].endsWith( ".txt" )) {
					testFileName = args[i+1];
				} else {
					testFileName = "testfile.txt";
					System.err.println( "Warning-- test file is testfile.txt");
				}
				i++;
			}
			
			if (arg.startsWith("-out")) {

				if (i + 1 >= args.length || args[i + 1] == null) {
					System.err
							.println("error: -outputGroup must be followed by output filename");
				} else {
					outputFileName = args[i + 1];
					String t = outputFileName.substring(0, outputFileName
							.lastIndexOf('.') - 1);
					outputDescName = t + "Desc.txt";
					i++;
				}
				continue;
			}

			if ( arg.startsWith( "-r" )) {
				remarks_dump_flag = true;
			}
			
			if (arg.startsWith("-mat")) {

				if (i + 1 >= args.length || args[i + 1] == null) {
					System.err
							.println("error: -outputMatrix must be followed by output filename");
				} else {
					matrixFileName = args[i + 1];
					i++;
				}
				continue;
			}
			// otherwise, ignore
		}

	}
	
	
	private void appendID(File file, RegRecord rec) {
		try {
			BufferedWriter out = new BufferedWriter(new FileWriter(file, true));
			out.write("\nID: " + rec.id());
			out.close();

		} catch (IOException e) {
			e.printStackTrace();

		}
	}

	void closeWriters() {

		if (outputWriter != null) {
			try {
				outputWriter.close();
				outputDescWriter.close();

			} catch (IOException exc) {
			}
		}

	}

	private int[][] flipComb(int[][] allM) {
		N = folks.size();
		int[][] nw = new int[N][N];
		for (int i = 0; i < N; i++) {
			for (int j = 0; j < N; j++) {
				nw[i][j] = Math.min(allM[i][j], allM[j][i]);
			}
		}
		return nw;
	}

	int[][] makeAllMatrix() {
		N = folks.size();
		int[][] m = new int[N][N];
		for (int i = 0; i < N; i++) {
			for (int j = 0; j < N; j++) {
				RegRecord who = folks.get(i);
				RegRecord targ = folks.get(j);

				m[i][j] = who.willDate(targ);

			}
		}
		return m;
	}

	int[][] makeMatrix(boolean gay_matrix) {
		N = folks.size();
		int[][] m = new int[N][N];
		for (int i = 0; i < N; i++) {
			for (int j = 0; j < N; j++) {
				if (i == j)
					continue;

				RegRecord who = folks.get(i);
				RegRecord targ = folks.get(j);
				if (who.isBi()) {
					if (gay_matrix) {
						if (who.bigendered() || who.ungendered()) {
							m[i][j] = (targ.isFemale() || targ.ungendered()) ? who
									.willDate(targ)
									: 0;
						} else {
							m[i][j] = who.genderMatch(targ)
									|| targ.ungendered() ? who.willDate(targ)
									: 0;
						}
					} else {
						if (who.bigendered() || who.ungendered()) {
							m[i][j] = targ.isMale() || targ.ungendered() ? who
									.willDate(targ) : 0;
						} else {
							m[i][j] = who.genderCross(targ)
									|| targ.ungendered() ? who.willDate(targ)
									: 0;
						}
					}
				} else {
					m[i][j] = who.willDate(targ);
				}
			}
		}
		return m;
	}

	public void match(ArrayList<RegRecord> folks) {

		this.folks = folks;

		setupWriters();

		writeDescription();

		writeMatrices();

		closeWriters();
		
		if ( testFileName != null )
			testSample( testFileName );
	}

	private void writeDescription() {

		System.err.println( "Writing descriptions of folks");
		Collections.sort( folks, new Comparator<RegRecord>() {

			public int compare(RegRecord r1, RegRecord r2) {
				if (r1.timestamp == r2.timestamp) return 0;
				return r1.timestamp > r2.timestamp ? -1 : 1;
			}
			
		});
	
		for (RegRecord reg : folks) {
			try {
				System.out.print( reg.id() + " ");
					outputWriter
							.write(reg.toString(write_comments_flag) + "\n");

					if (remarks_dump_flag) {
						System.out.println(reg.info());
						outputDescWriter.write(reg.nameDesc() + "\n\n");
					} else {
						// System.out.println( reg.id() + " is " + reg.geekCode() +
						// "\n" );
						outputDescWriter.write(reg.entry(true) + "\n\n");
					}
			

			} catch (IOException exc) {
				System.err.println("Output file IOException, bailing out: "
						+ outputFileName);
				exc.printStackTrace(System.err);
				outputError = true;

			}
		}
		System.out.println("");

	}

	void setupWriters() {

		try {

			outputFile = new File(outputFileName);
			outputDesc = new File(outputDescName);

			if (outputFile.exists() && !outputFile.canWrite()) {
				System.err.println("Unable to write output file, bailing out: "
						+ outputFileName);
				outputError = true;
				return;
			}
			if (outputDesc.exists() && !outputDesc.canWrite()) {
				System.err.println("Unable to write output file, bailing out: "
						+ outputDescName);
				outputError = true;
				return;
			}

			outputWriter = new PrintWriter(outputFile);
			outputDescWriter = new PrintWriter(outputDesc);

			if (write_comments_flag) {
				outputWriter.write(HEADER_STRING_ALL);
			} else {
				outputWriter.write(HEADER_STRING_SIMPLE);
			}
			outputWriter.write("\n");

		} catch (IOException exc) {
			System.err.println("Output file IOException, bailing out: "
					+ outputFileName);
			exc.printStackTrace(System.err);
			outputError = true;
			return;
		}

	}

	void writeMatrices() {

		int[][] gm = makeMatrix(true);
		writeOneMatrix("gay-" + matrixFileName, gm);

		int[][] sm = makeMatrix(false);
		writeOneMatrix("str-" + matrixFileName, sm);

		allM = makeAllMatrix();
		writeOneMatrix("all-" + matrixFileName, allM);

		// double check that everything in all is in either gm or sm
		for (int i = 0; i < N; i++) {
			for (int j = 0; j < N; j++) {
				if (i == j) {
					continue;
				}
				if (allM[i][j] > 0 && !(gm[i][j] > 0 || sm[i][j] > 0)) {
					throw new RuntimeException(
							"The Matrix alternation failed to capture all potential matches.");
				}
			}
		}
		writeOneMatrix("mut-" + matrixFileName, flipComb(allM));
	}

	void writeMatrix(Writer matrixWriter, int[][] m) {
		try {
			for (int i = 0; i < N; i++) {

				matrixWriter.write("," + folks.get(i).id());
			}
			matrixWriter.write("\n");
			for (int i = 0; i < N; i++) {
				matrixWriter.write(folks.get(i).id());
				for (int j = 0; j < N; j++) {
					matrixWriter.write("," + m[i][j]);
				}
				matrixWriter.write("\n");
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	void writeOneMatrix(String filename, int[][] m) {
		try {

			Writer matrixWriter = null;
			matrixFile = new File(filename);
			if (matrixFile.exists() && !matrixFile.canWrite()) {
				System.err.println("Unable to write matrix file, bailing out: "
						+ filename);
				outputError = true;
				return;
			}

			matrixWriter = new PrintWriter(matrixFile);

			writeMatrix(matrixWriter, m);

			matrixWriter.close();
		} catch (IOException e) {
			e.printStackTrace();
			System.err.println("failed to write out date matrices.");
		}
	}

	String yesBlank(boolean val) {
		return (val) ? "yes" : "";
	}

	String yesNo(boolean val) {
		return (val) ? "yes" : "no";
	}

	String yesNoBlank(int val) {
		switch (val) {
		case Matcher.YES:
			return "yes";
		case Matcher.NO:
			return "no";
		case Matcher.UNKNOWN:
			return "";
		default:
			throw new RuntimeException("Illegal value for yesNoBlank: " + val);
		}
	}
	
	
	void testSample( String testFileName ) {

		try {

			File testFile = new File(testFileName);

			if (testFile.exists() && !testFile.canWrite()) {
				System.err.println("Unable to write output file, bailing out: "
						+ outputFileName);
				return;
			}

			PrintWriter wrt = new PrintWriter(testFile);
			
			Random r = new Random();
			
			for ( int i = 0; i < folks.size(); i++ ) {
				RegRecord a = folks.get(i);
				RegRecord b = folks.get(r.nextInt(folks.size()));
				wrt.print( "\n\n*******\n" + a.entry(false) + "\n --->  " 
						+ a.willDate(b) + " --- " + b.willDate(a) + " <---\n" + b.entry(false) + "\n");
			}
			
			
		} catch (IOException exc) {
			System.err.println("Output file IOException, bailing out: "
					+ outputFileName);
			exc.printStackTrace(System.err);
			outputError = true;
			return;
		}		
	}

}
